package com.api.model;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.time.Instant;

/**
 * @Classname NtpServer
 * @Description TODO
 * @Date 2023/10/26 10:57
 * @Created by XiongXiong
 * @Author: X.I.O
 */
public class NtpServer {

    private static final int NTP_PORT = 123;
    private static final int NTP_PACKET_SIZE = 48;
    private static final long NTP_TO_UNIX_EPOCH = 2208988800L;

    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(NTP_PORT);
            System.out.println("NTP server started on port " + NTP_PORT);

            while (true) {
                DatagramPacket requestPacket = new DatagramPacket(new byte[NTP_PACKET_SIZE], NTP_PACKET_SIZE);
                socket.receive(requestPacket);

                InetAddress clientAddress = requestPacket.getAddress();
                int clientPort = requestPacket.getPort();

                long currentTimeMillis = System.currentTimeMillis();
                long ntpTime = (currentTimeMillis / 1000) + NTP_TO_UNIX_EPOCH;

                byte[] responsePacket1 = createNtpResponsePacket(ntpTime);

                DatagramPacket responsePacket = new DatagramPacket(responsePacket1, responsePacket1.length,
                        clientAddress, clientPort);
                socket.send(responsePacket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }

    private static byte[] createNtpResponsePacket(long ntpTime) {
        byte[] packet = new byte[NTP_PACKET_SIZE];

        packet[0] = 0x1C; // Leap Indicator, Version, Mode
        packet[1] = 0x01; // Stratum
        packet[2] = 0x00; // Poll Interval
        packet[3] = (byte) 0xEC; // Precision

        // Root Delay and Dispersion
        packet[4] = 0x00;
        packet[5] = 0x00;
        packet[6] = 0x00;
        packet[7] = 0x00;

        // Reference Identifier
        packet[8] = 0x4E;
        packet[9] = 0x4F;
        packet[10] = 0x44;
        packet[11] = 0x45;

        // Reference Timestamp
        writeTimeStamp(packet, 16, ntpTime);

        // Origin Timestamp
        writeTimeStamp(packet, 24, ntpTime);

        // Receive Timestamp
        writeTimeStamp(packet, 32, ntpTime);

        // Transmit Timestamp
        writeTimeStamp(packet, 40, ntpTime);

        return packet;
    }

    private static void writeTimeStamp(byte[] packet, int offset, long ntpTime) {
        Instant instant = Instant.ofEpochSecond(ntpTime);
        long seconds = instant.getEpochSecond();
        long fraction = instant.getNano() * 0x100000000L / 1000000000L;

        packet[offset++] = (byte) (seconds >> 24);
        packet[offset++] = (byte) (seconds >> 16);
        packet[offset++] = (byte) (seconds >> 8);
        packet[offset++] = (byte) (seconds);

        packet[offset++] = (byte) (fraction >> 24);
        packet[offset++] = (byte) (fraction >> 16);
        packet[offset++] = (byte) (fraction >> 8);
        packet[offset++] = (byte) (fraction);
    }
}
